//*********************************************************************
//		Copyright (c) 2003 - 2008 Microsoft Corporation.
//      This code is provided under Microsoft Public License, which can be found at
//      http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx 
//
// @File: Stor.cs
//
// Purpose: SQL Connection wrapper
//
//
//
// Notes: 
//
//
//	
// History: 
//     @Version: V2.1
//
//     07/14/08 PGV 2.1.001.0 Starting V2.1 (based on 2.0.030.0 code)
//
//
// @EndHeader@
//*********************************************************************

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Text;
using System.Diagnostics;

namespace H2V2
{
	public enum ConnectionType
	{
		TARGET=0,
		REPOSITORY
	}

    public class GlobalEnvironment
    {
        public static bool RepositoryFailure = false;
    }

    public class StorageErrorException : Exception
	{
        public CollectorError m_Error;
		public int m_auxError;
		public ConnectionType m_type = ConnectionType.REPOSITORY;	// default

        public StorageErrorException (CollectorError a_Error, int a_auxError, string a_object)
            : base (a_object) 
		{
			m_Error=a_Error;
			m_auxError=a_auxError;
		}

        public StorageErrorException (CollectorError a_Error, int a_auxError, string a_object, Exception a_ex)
            : base (a_object, a_ex) 
		{
			m_Error=a_Error;
			m_auxError=a_auxError;
		}
        public StorageErrorException (CollectorError a_Error, int a_auxError, string a_object, ConnectionType a_type)
            : base (a_object) 
		{
			m_Error=a_Error;
			m_auxError=a_auxError;
			m_type = a_type;
		}

        public StorageErrorException (CollectorError a_Error, int a_auxError, string a_object, Exception a_ex, ConnectionType a_type)
            : base (a_object, a_ex) 
		{
			m_Error=a_Error;
			m_auxError=a_auxError;
			m_type = a_type;
		}
	}

	public enum H2ProductLevel
	{
        // mapped to values in d_syscurconfigs

		UNKNOWN = -1,
		PreSql2000SP3 = 0,
		Sql2000SP3 = 1,
        Sql2005 = 3,    // 2 was used for SQL2005 beta versions
        Sql2008 = 4
	}

	public struct SqlServerVersion 
	{
		public short major;
		public short minor;
		public short build;
		public string version;
		public string edition;
		public string level;
		public H2ProductLevel H2level;

        public void CalculateLevel ()
        {
            switch (major)
            {
                case 9:
                    H2level = H2ProductLevel.Sql2005;
                    break;
                case 10:
                    H2level = H2ProductLevel.Sql2008;
                    break;
                case 8:
                    H2level = (build >= 760) ?
                        H2level = H2ProductLevel.Sql2000SP3 : H2level = H2ProductLevel.PreSql2000SP3;
                    break;
                default:
                    H2level = H2ProductLevel.PreSql2000SP3;
                    break;
            }
        }
	}

	public class Connection : StatusObject
	{
		string m_ConnectionString ;
		SqlConnection m_Connection;
		public SqlConnection SQLConnection { get {return m_Connection;} }
		string m_sversion;
		SqlServerVersion m_ver;
		string m_server;
		SqlTransaction m_Transaction;
		bool m_bTransactionOpen;
		ConnectionType m_type = ConnectionType.TARGET;
		
		public SqlServerVersion Version { get {return m_ver;} }
		public H2ProductLevel ProductLevel { get {return m_ver.H2level;} set {m_ver.H2level = value;} }
		public void SetVersion (Version a_ver)
		{
			m_ver.major = (short)a_ver.Major;
			m_ver.minor = (short)a_ver.Minor;
			m_ver.build = (short)a_ver.Build;
			m_ver.version = a_ver.ToString ();
            if (String.IsNullOrEmpty (m_ver.edition))
            {
                m_ver.edition = "UNKNOWN";
            }
            if (String.IsNullOrEmpty (m_ver.level))
            {
                m_ver.level = "UNKNOWN";
            }


            m_ver.CalculateLevel ();
		}

		public Connection (ConnectionType a_type)
		{
			m_type = a_type;
		}

		public void Init (string a_ServerName, string a_connectionString)
		{
			string query;
			SqlDataReader dataReader;
			SqlCommand command;

			m_server = a_ServerName;

			m_ConnectionString = (null == a_connectionString) ? 			
				String.Format("server={0};Trusted_Connection=true;",a_ServerName) :
				a_connectionString;
			
			m_Connection = new SqlConnection (m_ConnectionString);
			m_Connection.InfoMessage  += new SqlInfoMessageEventHandler(OnInfoMessage);

			try 
			{
				m_Connection.Open();
				m_sversion = m_Connection.ServerVersion;
			}
			catch (SqlException sqlex)
			{
				ChangeStatus (sqlex);
				throw (new StorageErrorException (CollectorError.StorCannotConnect,sqlex.Number,m_ConnectionString,sqlex,m_type));
			}

			query = "Select serverproperty('edition'), serverproperty('productlevel'), serverproperty('productversion') ";
			command = new SqlCommand (query, m_Connection);
			dataReader = command.ExecuteReader ();

			try
			{
				dataReader.Read ();
				m_ver.edition = dataReader.GetString(0);
				m_ver.level =  dataReader.GetString(1);
				m_ver.version =  dataReader.GetString(2);

				m_ver.major = Convert.ToInt16 (m_Connection.ServerVersion.Substring (0,2));
				m_ver.minor = Convert.ToInt16 (m_Connection.ServerVersion.Substring (3,2));
				m_ver.build = Convert.ToInt16 (m_Connection.ServerVersion.Substring (6,4));

                m_ver.CalculateLevel ();
			}
			catch (Exception ex)
			{
				ChangeStatus (ex);
				m_Connection.Close ();
                throw (new StorageErrorException (CollectorError.StorDatareaderFailed, 1, query, ex, m_type));
			}
			finally
			{
				dataReader.Close ();
			}

		}

		public void Close ()
		{
			m_Connection.Close ();
		}
	
		protected static void OnInfoMessage(object sender, SqlInfoMessageEventArgs args)
		{
			string msg;
			
			foreach (SqlError err in args.Errors)
			{
				msg = "The "+err.Source+" has received a severity "+err.Class+", state "+err.State
					+" message "+err.Number;
				if (err.Procedure != "")
				{
					msg += " on line "+err.LineNumber+" of procedure "+err.Procedure;
				}
				if (err.Server != "")
				{
					msg += " on server "+err.Server;
				}

				msg += ": "+err.Message;

				Debug.WriteLine ("\tDEBUG: \t"+msg);
			}
		}

		public void TransactionOpen ()
		{
			m_Transaction = m_Connection.BeginTransaction ();
			m_bTransactionOpen = true;
		}

		public void TransactionCommit ()
		{
			m_Transaction.Commit ();
			m_bTransactionOpen = false;
		}

		public void TransactionRollback ()
		{
			m_Transaction.Rollback ();
			m_bTransactionOpen = false;
		}

		public int StoreNonQuery (string a_cmd)
		{
			SqlCommand sqlcmd = new SqlCommand ();
            sqlcmd.CommandText = a_cmd;

            return StoreNonQuery (sqlcmd);
		}

        public int StoreNonQuery (SqlCommand cmd)
        {
            int ret;

            cmd.Connection = m_Connection;
            cmd.CommandTimeout = Constants.RepositoryTimeout;

            if (m_bTransactionOpen)
            {
                cmd.Transaction = m_Transaction;
            }

            try
            {
                ret = cmd.ExecuteNonQuery ();
            }
            catch (SqlException sqlex)
            {
                ChangeStatus (sqlex);
                ChangeStatus (sqlex.StackTrace, StatusEventType.STACK_TRACE);
                if (m_type == ConnectionType.REPOSITORY)
                {
                    GlobalEnvironment.RepositoryFailure = true;
                }
                throw (new StorageErrorException (CollectorError.StorSyntaxErrorNonQuery, sqlex.Number, cmd.CommandText, sqlex, m_type));
            }

            return ret;
        }

        public object ExecuteScalar (string a_cmd)
		{
			object ret;
			SqlCommand cmd;

			cmd = new SqlCommand (a_cmd, m_Connection);
			try
			{
				ret = cmd.ExecuteScalar ();
			}
			catch (SqlException sqlex)
			{
				ChangeStatus (sqlex);
				ChangeStatus (sqlex.StackTrace, StatusEventType.STACK_TRACE);
				if (m_type == ConnectionType.REPOSITORY) 
				{
                    GlobalEnvironment.RepositoryFailure = true;
				}
                throw (new StorageErrorException (CollectorError.StorSyntaxErrorScalar, sqlex.Number, a_cmd, sqlex, m_type));
			}

			return ret;
		}
	}
}
